package org.micode.libs.remoteapi;

import org.micode.common.config.ApplicationConfig;
import org.micode.common.consts.HttpHeadConsts;
import org.micode.common.domain.ApiResult;
import org.micode.common.enums.ResultEnum;
import org.micode.common.exception.DataException;
import org.micode.common.exception.RemoteServiceException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.micode.common.logtrace.LogTracer;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;

import java.lang.reflect.Type;

@Slf4j
public class DefaultClient {

    /**
     * 远程服务地址属性名
     */
    private static final String REMOTE_SERVICE_ADDRESS_PROPERTY_NAME = "spring.application.remote.address";

    public DefaultClient() {
        HttpComponentsClientHttpRequestFactory httpRequestFactory = new HttpComponentsClientHttpRequestFactory();

        httpRequestFactory.setConnectionRequestTimeout(10000);
        httpRequestFactory.setConnectTimeout(10000);
        httpRequestFactory.setReadTimeout(10000);

        this.restTemplate = new RestTemplate(httpRequestFactory);
    }

    private RestTemplate restTemplate;

    /**
     * 请求
     *
     * @param url
     * @param data
     * @return
     */
    public <T> ApiResult<T> request(String serviceName, String url, Object data, Type returnType) {
        String remoteServiceAddress = getRemoteServiceAddress(serviceName);
        String requestUrl = remoteServiceAddress + url;
        return request(requestUrl, data, returnType);
    }

    /**
     * 请求
     *
     * @param requestUrl 完整的地址
     * @param data
     * @param returnType
     * @param <T>
     * @return
     */
    public <T> ApiResult<T> request(String requestUrl, Object data, Type returnType) {
        ApiResult apiResult = null;
        try {
            if (log.isDebugEnabled()) {
                log.debug("Call the remote service: " + requestUrl + "," + data);
            }
            HttpHeaders requestHeaders = new HttpHeaders();
            requestHeaders.set(HttpHeadConsts.REQUEST_HEADER_TIMESTAMP_KEY,
                    String.valueOf(System.currentTimeMillis()));
            requestHeaders.set(HttpHeadConsts.REQUEST_HEADER_SERVICE_NAME_KEY,
                    ApplicationConfig.SERVICE_NAME);

            // 添加日志跟踪ID
            requestHeaders.set(HttpHeadConsts.REQUEST_HEADER_REQUESTID_KEY, LogTracer.getTraceId());

            log.debug("returnType is {}", returnType.getTypeName());
            HttpEntity<?> httpEntity = new HttpEntity<>(data, requestHeaders);
            ResponseEntity<ApiResult<T>> responseEntity = this.restTemplate.exchange(
                    requestUrl, HttpMethod.POST, httpEntity,
                    ParameterizedTypeReference.forType(returnType));
            apiResult = responseEntity.getBody();
            if (log.isDebugEnabled()) {
                log.debug("Result of the remote service: " + requestUrl + "," + apiResult);
            }
        } catch (Exception e) {
            log.warn("Call the remote service failed: {}", requestUrl, e);
            throw new RemoteServiceException("Call the remote service failed");
        }

        //判断状态,1-999为系统异常，直接抛出，1000及以上为内部异常，不抛出，由业务层判断
        if (!(apiResult.getCode() == ResultEnum.SUCCESS.getCode()) &&
                (apiResult.getCode() <= ResultEnum.UNKNOWN.getCode())) {
            log.error("Call the remote service failed:" + requestUrl + ":" + apiResult.getMsg());
            throw new DataException(apiResult.getCode(), apiResult.getMsg());
        }

        return apiResult;
    }

    /**
     * 获取远程服务地址
     *
     * @param serviceName
     * @return
     */
    public String getRemoteServiceAddress(String serviceName) {
        String address = ApplicationConfig.readString(
                REMOTE_SERVICE_ADDRESS_PROPERTY_NAME + "." + serviceName,
                "");
        if (StringUtils.isBlank(address)) {
            log.error("The address of the remote service is not configured: serviceName={}", serviceName);
            throw new RemoteServiceException("Remote service configuration error");
        }
        return address;
    }

}
